package com.datou.datouspringmvc.servlet;

import com.datou.datouspringmvc.annotation.Controller;
import com.datou.datouspringmvc.annotation.RequestMapping;
import com.datou.datouspringmvc.annotation.RequestParam;
import com.datou.datouspringmvc.annotation.ResponseBody;
import com.datou.datouspringmvc.context.DatouWebApplicationContext;
import com.datou.datouspringmvc.handler.DatouHandler;
import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 充当原生的DispatcherServlet
 * 本质还是Servlet
 */
public class DatouDispatcherServlet extends HttpServlet {

    // 存放DatouHandler【url和控制器的映射关系】
    private List<DatouHandler> handlerList = new ArrayList<>();

    // 自己的spring容器
    DatouWebApplicationContext datouWebApplicationContext;

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        // 获取到web.xml文件中的 init-param
        String contextConfigLocation = servletConfig.getInitParameter("contextConfigLocation");

        datouWebApplicationContext = new DatouWebApplicationContext(contextConfigLocation);
        // 加载所有控制器，并初始化IOC容器
        datouWebApplicationContext.init();

        // 调用initHandlerMapping方法，初始化url 与 Handler 的映射关系
        initHandlerMapping(servletConfig.getServletContext().getContextPath());

        System.out.println("handlerList = " + handlerList);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        System.out.println("DatouDispatcherServlet doPost");

        // 调用方法，完成分发请求
        executeDispatcher(req, resp);
    }

    // 完成url 和 控制器的映射关系
    public void initHandlerMapping(String prefix) {
        if(datouWebApplicationContext.ioc.isEmpty()) {
            return;
        }

        for (Map.Entry<String, Object> entry : datouWebApplicationContext.ioc.entrySet()) {
            // 取出注入Object的class对象
            Class<?> aClass = entry.getValue().getClass();
            if (aClass.isAnnotationPresent(Controller.class)) {
                // 取出其所有方法
                Method[] declaredMethods = aClass.getDeclaredMethods();
                for (Method method : declaredMethods) {
                    // 判断是否有RequestMapping注解
                    if (method.isAnnotationPresent(RequestMapping.class)) {
                        RequestMapping annotation = method.getAnnotation(RequestMapping.class);
                        // 获取url /datou_springmvc/monster/list
                        String url = prefix + annotation.value();
                        DatouHandler datouHandler = new DatouHandler(url, entry.getValue(), method);
                        handlerList.add(datouHandler);
                    }
                }
            }
        }
    }

    // 通过request对象，返回对应的Controller方法
    public DatouHandler getHandler(HttpServletRequest req) {
        String requestURI = req.getRequestURI();
        for (DatouHandler datouHandler : handlerList) {
            if (datouHandler.getUrl().equals(requestURI)) {
                return datouHandler;
            }
        }
        return null;
    }

    // 完成分发请求
    public void executeDispatcher(HttpServletRequest req, HttpServletResponse resp) {
        DatouHandler handler = getHandler(req);
        try {
            if(handler == null) {
                resp.getWriter().write("<h1>404 NOT FOUND</h1>");
            } else {
                // 1.获取目标方法的所有形参参数信息【对用的数组】
                Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();

                // 2.创建实参数组，将对应的参数放入实参数组中,后面反射调用目标方法时会用到
                Object[] params = new Object[parameterTypes.length];
                // 遍历形参数组，将对应的实参填入到实参数组中
                for (int i = 0; i < parameterTypes.length; i ++) {
                    Class<?> parameterType = parameterTypes[i];
                    if("HttpServletRequest".equals(parameterType.getSimpleName())) {
                        params[i] = req;
                    } else if("HttpServletResponse".equals(parameterType.getSimpleName())) {
                        params[i] = resp;
                    }
                }
                // 处理提交时中文乱码
                req.setCharacterEncoding("UTF-8");
                Map<String, String[]> parameterMap = req.getParameterMap();

                for(Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                    String name = entry.getKey();
                    String value = entry.getValue()[0];

                    int indexRequestParameterIndex = getIndexRequestParameterIndex(handler.getMethod(), name);
                    if (indexRequestParameterIndex != -1) {
                        params[indexRequestParameterIndex] = value;
                    } else {
                        // 1.先得到目标方法的所有形参的名字 -》 方法获取形参名
                        // 2.对得到的形参数组进行遍历、比对
                        throw new RuntimeException("参数错误");
                    }

                }

                // 这样的写法是针对目标方法为m(HttpServletRequest request, HttpServletResponse response)
//                handler.getMethod().invoke(handler.getController(), req, resp);

                // 反射调用目标方法,执行对应的方法
                Object result = handler.getMethod().invoke(handler.getController(), params);


                // 对返回的结果进行解析
                if (result instanceof String) {
                    // 进行视图解析
                    String viewName = (String) result;
                    if (viewName.contains(":")) {
                        // 跳转的方式 forward/redirect
                        String viewType  = viewName.split(":")[0];
                        System.out.println("viewType = " + viewType);
                        // 跳转的页面
                        String viewPage  = viewName.split(":")[1];
                        System.out.println("viewPage = " + viewPage);
                        if ("forward".equals(viewType)) {
                            // forward
                            req.getRequestDispatcher(viewPage).forward(req, resp);
                        } else if("redirect".equals(viewType)) {
                            // redirect
                            resp.sendRedirect(req.getContextPath() + viewPage);
                        }
                    } else {
                        req.getRequestDispatcher(viewName).forward(req, resp);
                    }

                } else if(result instanceof ArrayList) {
                    // 返回json格式数据
                    Method method = handler.getMethod();
                    // 判断目标方法是否有ResponseBody注解
                    if (method.isAnnotationPresent(ResponseBody.class)) {
                        resp.setContentType("application/json;charset=utf-8");
                        // 将result转换为json格式
                        ObjectMapper objectMapper = new ObjectMapper();
                        String resultJson = objectMapper.writeValueAsString(result);
                        PrintWriter writer = resp.getWriter();
                        writer.write(resultJson);
                        writer.flush();
                        writer.close();

                    }

                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    public int getIndexRequestParameterIndex(Method method, String name) {
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i ++) {
            Parameter parameter = parameters[i];
            if (parameter.isAnnotationPresent(RequestParam.class)) {
                String value = parameter.getAnnotation(RequestParam.class).value();
                if(name.equals(value)) {
                    return i;
                }
            } else if(parameter.getName().equals(name))
                // 无@RequestParam注解的参数
                return i;
        }

        return -1;
    }

//    // 得到目标方法的所有形参名称，并放入到集合中返回
//    public List<String> getParameterNames(Method method) {
//        List<String> parameterNames = new ArrayList<>();
//        // 得到所有参数名称->这里的名称不是真正名称
//        // 在默认情况下， parameter.getName() 的哦搭配的名字不是形参真正的名字
//        // 而是 [arg0, arg1, arg2 ...]
//        Parameter[] parameters = method.getParameters();
//
//        return null;
//    }
}
